package com.zf.fastexcel.utils;

import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcel;
import cn.idev.excel.write.metadata.WriteSheet;
import com.zf.fastexcel.utils.handler.CustomAutoColumnWidthWriteHandler;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.http.HttpHeaders;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author Zhou Feng
 */
public class ExcelExportUtils {
    private static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    private static final String ENCODE = StandardCharsets.UTF_8.name();


    /**
     * 导出时封装response
     *
     * @param exportFileName 导出的文件名
     * @param response       响应对象
     */
    public static void decorateResponse(String exportFileName, HttpServletResponse response) {
        response.setContentType(CONTENT_TYPE);
        response.setCharacterEncoding(ENCODE);
        // 这里URLEncoder.encode可以防止中文乱码
        String fileName = URLEncoder.encode(exportFileName, StandardCharsets.UTF_8)
                .replaceAll("\\+", "%20");
        // 暴露出 Content-Disposition 响应头，防止由于跨域而访问不到
        response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
        // 设置 Content-Disposition 响应头的值
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename*=utf-8''" + fileName + ".xlsx");
    }

    /**
     * 单个sheet: 导出到excel-支持自动列宽
     *
     * @param fileName 文件名
     * @param response 响应
     * @param head     头
     * @param data     导出数据
     */
    public static <T> void exportToExcel(String fileName, HttpServletResponse response, Class<T> head, List<T> data) throws IOException {
        ExcelExportUtils.decorateResponse(fileName, response);
        //默认导出到第一个sheet
        FastExcel.write(response.getOutputStream(), head)
                .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                .sheet(0)
                .doWrite(data);
    }

    /**
     * 单个sheet: 导出到 byte[]-支持自动列宽
     *
     * @param head 头
     * @param data 导出数据
     */
    public static <T> byte[] exportToByte(Class<T> head, List<T> data) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //默认导出到第一个sheet
        FastExcel.write(outputStream, head)
                .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                .sheet(0)
                .doWrite(data);
        return outputStream.toByteArray();
    }

    /**
     * 单个sheet: 动态表头导出到excel-支持自动列宽
     *
     * @param fileName 文件名
     * @param response 响应
     * @param head     头
     * @param data     导出数据
     */
    public static <V> void exportToExcelDynamicHead(String fileName, HttpServletResponse response, List<List<String>> head, List<List<V>> data) throws IOException {
        ExcelExportUtils.decorateResponse(fileName, response);
        //默认导出到第一个sheet
        FastExcel.write(response.getOutputStream())
                .head(head)
                .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                .sheet(0)
                .doWrite(data);

    }

    /**
     * 单个sheet: 动态表头导出到 byte[] -支持自动列宽
     *
     * @param head 头
     * @param data 导出数据
     */
    public static <V> byte[] exportToByteDynamicHead(List<List<String>> head, List<List<V>> data) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //默认导出到第一个sheet
        FastExcel.write(outputStream)
                .head(head)
                .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                .sheet(0)
                .doWrite(data);
        return outputStream.toByteArray();
    }


    /**
     * 多个sheet: 动态表头导出到excel-支持自动列宽
     *
     * @param fileName 文件名
     * @param response 响应
     * @param head     头, sheetName -> header
     * @param data     导出数据  sheetName -> data
     */
    public static <V> void exportToExcelDynamicHeadMultiSheet(String fileName, HttpServletResponse response, Map<String, List<List<String>>> head, Map<String, List<List<V>>> data) throws IOException {
        ExcelExportUtils.decorateResponse(fileName, response);
        // 创建 ExcelWriter
        ExcelWriter excelWriter = FastExcel.write(response.getOutputStream())
                .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                .build();

        try (excelWriter) {
            for (Map.Entry<String, List<List<V>>> dataEntry : data.entrySet()) {
                // 创建WriteSheet, 并使用 ExcelWriter 向其中写入数据
                WriteSheet writeSheet = FastExcel.writerSheet(dataEntry.getKey())
                        .head(head.get(dataEntry.getKey()))
                        .build();
                excelWriter.write(dataEntry.getValue(), writeSheet);
            }
        }
    }


    /**
     * 多个sheet: 动态表头导出到 byte[] -支持自动列宽
     *
     * @param head 头, sheetName -> header
     * @param data 导出数据  sheetName -> data
     */
    public static <V> byte[] exportToByteDynamicHeadMultiSheet(Map<String, List<List<String>>> head, Map<String, List<List<V>>> data) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 创建 ExcelWriter
        ExcelWriter excelWriter = FastExcel.write(outputStream)
                .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                .build();

        try (excelWriter) {
            for (Map.Entry<String, List<List<V>>> dataEntry : data.entrySet()) {
                // 创建WriteSheet, 并使用 ExcelWriter 向其中写入数据
                WriteSheet writeSheet = FastExcel.writerSheet(dataEntry.getKey())
                        .head(head.get(dataEntry.getKey()))
                        .build();
                excelWriter.write(dataEntry.getValue(), writeSheet);
            }
        }
        return outputStream.toByteArray();
    }


    /**
     * 单个sheet: 动态表头导出到excel-传入合并信息
     *
     * @param fileName             文件名
     * @param response             响应
     * @param head                 表头
     * @param data                 数据
     * @param cellRangeAddressList 合并信息
     */
    public static <V> void exportToExcelDynamicHeaderWithMergeInfo(String fileName, HttpServletResponse response, List<List<String>> head, List<List<V>> data, List<CellRangeAddress> cellRangeAddressList) throws IOException {
        // excel表
        ExcelExportUtils.decorateResponse(fileName, response);
        // 创建 ExcelWriter
        ExcelWriter excelWriter = FastExcel.write(response.getOutputStream()).build();
        try (excelWriter) {
            // 创建WriteSheet, 并使用 ExcelWriter 向其中写入数据
            WriteSheet writeSheet = FastExcel.writerSheet(0)
                    .head(head)
                    .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                    .build();
            // 写入数据
            excelWriter.write(data, writeSheet);

            // 获取当前正在操作的sheet
            Sheet sheet = excelWriter.writeContext().writeSheetHolder().getSheet();
            // 设置合并信息
            if (CollectionUtils.isNotEmpty(cellRangeAddressList)) {
                for (CellRangeAddress cellAddresses : cellRangeAddressList) {
                    sheet.addMergedRegionUnsafe(cellAddresses);
                }
            }
        }
    }


    /**
     * 单个sheet: 动态表头导出到 byte[] -传入合并信息
     *
     * @param head                 表头
     * @param data                 数据
     * @param cellRangeAddressList 合并信息
     */
    public static <V> byte[] exportToByteDynamicHeaderWithMergeInfo(List<List<String>> head, List<List<V>> data, List<CellRangeAddress> cellRangeAddressList) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 创建 ExcelWriter
        ExcelWriter excelWriter = FastExcel.write(outputStream).build();
        try (excelWriter) {
            // 创建WriteSheet, 并使用 ExcelWriter 向其中写入数据
            WriteSheet writeSheet = FastExcel.writerSheet(0)
                    .head(head)
                    .registerWriteHandler(new CustomAutoColumnWidthWriteHandler())
                    .build();
            // 写入数据
            excelWriter.write(data, writeSheet);

            // 获取当前正在操作的sheet
            Sheet sheet = excelWriter.writeContext().writeSheetHolder().getSheet();
            // 设置合并信息
            if (CollectionUtils.isNotEmpty(cellRangeAddressList)) {
                for (CellRangeAddress cellAddresses : cellRangeAddressList) {
                    sheet.addMergedRegion(cellAddresses);
                }
            }
        }
        return outputStream.toByteArray();
    }

    public static void main(String[] args) throws IOException {
        ArrayList<List<String>> head = new ArrayList<>();
        head.add(Arrays.asList("头1", "子1"));
        head.add(Arrays.asList("头1", "子2"));
        head.add(Arrays.asList("头2", "头2"));

        ArrayList<List<String>> data = new ArrayList<>();
        data.add(Arrays.asList("r1-co1", "r1-co2", "r1-co3"));
        data.add(Arrays.asList("r2-co1", "r2-co2", "r2-co3"));

        byte[] bytes = exportToByteDynamicHeaderWithMergeInfo(head, data, null);

        Files.write(Paths.get("D:\\test.xlsx"), bytes);

        // List<CellRangeAddress> cellRangeAddresses = Arrays.asList(
        //         new CellRangeAddress(2, 2, 0, 1)
        // );
        // byte[] bytes = exportToByteDynamicHeaderWithMergeInfo(head, data, cellRangeAddresses);
        // Files.write(Paths.get("D:\\test.xlsx"), bytes);
    }

}
